//
// arith.cpp
// libprotea
// Tools Library for the Protea Project / Arithmatic functions for Fields
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
// Arithmatic functions
//
#include "../defines.h"
#include "../Buffer/Buffer.h"
#include "../AppHandler/AppInterface.h"
#include "../Security/hash.h"
#include "fields.h"
#include "field_defns.h"
#include <limits.h>
#include <math.h>

#include "../defns.h"
using namespace ProteaApp1;

// Used for comparisons between double values
const double EPSILON = 0.00000001;

enum t_oper_type {
	ADD_OPER = 1,
	SUB_OPER = 2,
	MULT_OPER = 3,
	DIV_OPER = 4,
	COMP_OPER = 5,
	ABS_OPER = 6
};




// Handles all floating point arithmetic
inline double ArithDouble( double parm1, double parm2, t_oper_type operation, int dec_places ) {
	double result=0;
	bool rounding = true;

	switch( operation ) {
		case ADD_OPER:
			result = (parm1 + parm2);
			break;

		case SUB_OPER:
			result = (parm1 - parm2);
			break;

		case MULT_OPER:
			result = (parm1 * parm2);
			break;

		case DIV_OPER:
			result = (parm1 / parm2);
			break;

		case COMP_OPER:
			if (parm1 > parm2)
				result = -1;
			else if (parm1 < parm2)
				result = 1;
			else
				result = 0;
			rounding = false;
			break;

		case ABS_OPER:
			result = fabs( parm1 );
			rounding = false;
			break;
	};

	// Do we need to round the results?
	if (rounding && dec_places >= 0) {
		double pow10 = pow( 10.0, dec_places );
		double temp = result * pow10;
		double frac = modf( temp, &result );

		// Now determine rounding, based on fractional part
		if (result >= 0) {
			if (frac > 0.5) {
				// Round up
				result = result + 1;
			};
		} else if (frac < -0.5) {
			// Round down
			result = result - 1;
		};

		// Go back to original scale
		result = result / pow10;
	};

	return result;
};





// Handles all integer arithmetic
inline long ArithLong( long parm1, long parm2, t_oper_type operation ) {
	INFUNC( ArithLong, NULL );

	int field = 0;
	long result;

	switch( operation ) {
		case ADD_OPER:
			result = parm1 + parm2;

			// Check for underflow/overflow
			if (parm1 > 0 && parm2 > 0 && result < 0)
				RAISEFIELDERROR( ERR_UNDERFLOW, field )
			else if (parm1 < 0 && parm2 < 0 && result > 0)
				RAISEFIELDERROR( ERR_OVERFLOW, field )
			break;

		case SUB_OPER:
			result = parm1 - parm2;

			// Check for underflow/overflow
			if (parm1 > 0 && parm2 < 0 && result < 0)
				RAISEFIELDERROR( ERR_UNDERFLOW, field )
			else if (parm1 < 0 && parm2 > 0 && result > 0)
				RAISEFIELDERROR( ERR_OVERFLOW, field )

			break;

		case MULT_OPER: {
			char sign1, sign2, sign3;

			result = parm1 * parm2;

			sign1 = parm1 >= 0;
			sign2 = parm2 >= 0;
			sign3 = result >= 0;

			// Check to see that the sign changed correctly
			if (sign1 % sign2 != sign3)
				RAISEFIELDERROR( ERR_OVERFLOW, field );

			break;
		};

		case DIV_OPER:
			if (parm2 == 0)
				RAISEFIELDERROR( 1, field );
			result = parm1 / parm2;
			break;

		case COMP_OPER:
			if (parm1 > parm2)
				result = -1;
			else if (parm1 < parm2)
				result = 1;
			else
				result = 0;

			break;

		case ABS_OPER:
			result = abs( parm1 );
			break;

		default:
			result = 0;
			RAISEFIELDERROR( 1, field );
	};

	OUTFUNCRET( result )
};





//
// DoMath
//
//	This is the meat of the arithmetic process.  You pass in two source
// fields and a destination field.
//
inline void DoMath( XCField* result, const XCField* parm1, const XCField* parm2, t_oper_type oper ) {
	INFUNC( DoMath, NULL )

	int field = result->GetFieldNumber();

	// Select on result type
	switch( result->GetType() ) {
		case D_INTEGER:
		case S_INTEGER: 
		case AUTONUMBER: {
			// Do all arithmetic in a long value and check for overflow
			long value;

			// TODO: Fix rounding problems right here!!!!!!!!!!!
			value = ArithLong( parm1->GetLong(true), parm2->GetLong(true), oper );

			if (result->GetType() == S_INTEGER) {
				// Check for overflow/underflow
				if (value > SHRT_MAX)
					RAISEFIELDERROR( ERR_OVERFLOW, field )
				else if (value < SHRT_MIN)
					RAISEFIELDERROR( ERR_UNDERFLOW, field )

				result->SetShort( (short int)value );
			} else
				result->SetLong( value );
			break;
		};

		case DEC_FLOAT:
			result->SetFloat( (float)ArithDouble( parm1->GetFloat(), parm2->GetFloat(), oper, result->GetDecPlaces() ) );
			break;

		case DEC_DOUBLE:
			result->SetDouble( ArithDouble( parm1->GetDouble( true ), parm2->GetDouble( true ), oper, result->GetDecPlaces() ) );
			break;

		case DUR_HH_TIME_DISP:
		case DUR_HM_TIME_DISP:
			result->DoubleToDuration( ArithDouble( parm1->GetDouble( true ), parm2->GetDouble( true ), oper, -1 ) );
			break;

		default:
			RAISEFIELDERROR( ERR_BAD_ARITH_OPER, field );
	};

	OUTFUNC()
};


void XCField::Abs() {
	INFUNC( XCField::Abs, owner->APPDIC )
		DoMath( this, this, this, ABS_OPER );
	OUTFUNC()
};



void XCField::Add( const XCField* value ) {
	INFUNC( XCField::Add, owner->APPDIC )

	if (value) {
		owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Add", owner? owner->GetFormatNumber() : -1 );
		DoMath( this, this, value, ADD_OPER );
	};
	
	OUTFUNC()
};

void XCField::Add( const XCField* parm1, const XCField* parm2 ) {
	INFUNC( XCField::Add, owner->APPDIC )

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Add", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, parm1, parm2, ADD_OPER );

	OUTFUNC()
};




void XCField::Sub( const XCField* value ) {
	INFUNC( XCField::Sub, owner->APPDIC )
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Sub", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, this, value, SUB_OPER );
	OUTFUNC()
};

void XCField::Sub( const XCField* parm1, const XCField* parm2 ) {
	INFUNC( XCField::Sub, owner->APPDIC )
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Sub", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, parm1, parm2, SUB_OPER );
	OUTFUNC()
};




void XCField::Mult( const XCField* value ) {
	INFUNC( XCField::Mult, owner->APPDIC )
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Mult", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, this, value, MULT_OPER );
	OUTFUNC()
};

void XCField::Mult( const XCField* parm1, const XCField* parm2 ) {
	INFUNC( XCField::Mult, owner->APPDIC )

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Mult", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, parm1, parm2, MULT_OPER );

	OUTFUNC()
};




void XCField::Div( const XCField* value ) {
	INFUNC( XCField::Div, owner->APPDIC )
	
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Div", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, this, value, DIV_OPER );

	OUTFUNC()
};

void XCField::Div( const XCField* parm1, const XCField* parm2 ) {
	INFUNC( XCField::Div, owner->APPDIC )
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d) Div", owner? owner->GetFormatNumber() : -1 );
	DoMath( this, parm1, parm2, DIV_OPER );
	OUTFUNC()
};






//
// Constant arithmetic
//
inline void ConstArith( XCField* source, const int value, t_oper_type oper ) {
	INFUNC( DoMath, NULL )

	int field = source->GetFieldNumber();

	switch( source->GetType() ) {
		case S_INTEGER: {
			long result;

			result = ArithLong( source->GetShort(), value, oper );

			// Check for overflow/underflow
			if (result > SHRT_MAX)
				RAISEFIELDERROR( ERR_OVERFLOW, field )
			else if (result < SHRT_MIN)
				RAISEFIELDERROR( ERR_UNDERFLOW, field )

			source->SetShort( (short int)result );
			break;
		};

		case D_INTEGER :
		case AUTONUMBER :
			source->SetLong( ArithLong( source->GetLong(), value, oper ) );
			break;

		case DEC_FLOAT:
			source->SetFloat( (float)ArithDouble( source->GetFloat(), value, oper, source->GetDecPlaces() ) );
			break;

		case DEC_DOUBLE:
			source->SetDouble( ArithDouble( source->GetDouble(), value, oper, source->GetDecPlaces() ) );
			break;

		case DISPLAY_NUM:
			source->SetLong( ArithLong( source->GetLong( true ), value, oper ) );
			break;

		default:
			RAISEFIELDERROR( -2, field );
	};

	OUTFUNC();
};



void XCField::MultConst( const int value ) {
	INFUNC( XCField::MultConst, owner->APPDIC );
	ConstArith( this, value, MULT_OPER );
	OUTFUNC();
};

void XCField::DivConst( const int value ) {
	INFUNC( XCField::DivConst, owner->APPDIC );
	ConstArith( this, value, DIV_OPER );
	OUTFUNC();
};

void XCField::SubConst( const int value ) {
	INFUNC( XCField::SubConst, owner->APPDIC );
	ConstArith( this, value, SUB_OPER );
	OUTFUNC();
};

void XCField::AddConst( const int value ) {
	INFUNC( XCField::AddConst, owner->APPDIC )
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) AddConst: %d", owner ? owner->GetFormatNumber() : -1, number, value );
	ConstArith( this, value, ADD_OPER );
	OUTFUNC()
};




int XCField::CompareField( const XCField* value ) const {
	EXT_INFUNC( XCField::CompareField, owner->APPDIC );

	assert( value );

	int result;
	int comptype = -1;
	double dval1 = 0;
	double dval2 = 0;
	long lval1 = 0;
	long lval2 = 0;
	char *sval1 = NULL;
	char *sval2 = NULL;
	long int *bval1 = NULL;
	long int *bval2 = NULL;
	int ssize = 0;
	int tsize = 0;

	if (type == value->type) {
		switch( type ) {
			case STRING:
			case BIG_STRING:
			case VAR_STRING:
			case ALPHA_STRING:
			case LITERAL_STRING:
				sval1 = buffer_offset;
				sval2 = value->buffer_offset;

				ssize = CalcStringSize();
				tsize = value->CalcStringSize();

				comptype = 0;
				break;

			case XCBOOL:
				lval1 = GetLong( true );
				lval2 = value->GetLong( true );
				comptype = 3;
				break;

			case S_INTEGER:
			case D_INTEGER:
			case LITERAL_VAL:
			case AUTONUMBER:
				lval1 = GetLong( true );
				lval2 = value->GetLong( true );
				comptype = 1;
				break;

			case DATE_JULIAN:
			case DEC_FLOAT:
			case DEC_DOUBLE:
			case DISPLAY_NUM:
				dval1 = GetDouble( true );
				dval2 = value->GetDouble( true );
				comptype = 2;
				break;

			case XCBLOB:
				bval1 = GetBLOB();
				bval2 = value->GetBLOB();
				ssize = GetBLOBSize();
				tsize = value->GetBLOBSize();
				comptype = 4;
				break;

			default:
				if (data_type_defns[ type ].type_group & TYPE_DATE ||
					data_type_defns[ type ].type_group & TYPE_TIME) {

					// Dates/times can be handled as julian values
					dval1 = ToJulian();
					dval2 = value->ToJulian();
					comptype = 2;
				} else if (data_type_defns[ type ].type_group & TYPE_DUR &&
					data_type_defns[ value->type ].type_group & TYPE_DUR) {

					// Convert to double values
					dval1 = DurationToDouble();
					dval2 = value->DurationToDouble();
					comptype = 2;
				};
		};
	} else if (type == DEC_DOUBLE || type == DEC_FLOAT || value->type == DEC_DOUBLE || value->type == DEC_FLOAT) {
		dval1 = GetDouble( true );
		dval2 = value->GetDouble( true );

		comptype = 2;
	} else if ((type == LITERAL_STRING || type == STRING) && value->type == LITERAL_VAL) {
		long lit_val = value->GetLong( true );
		if (lit_val != NOT_IN_LIT_GROUP) {
			XCField* temp = owner->APPDIC->FromLitGroup( value->GetLong(true), value->GetDecPlaces() );
			if (!temp)
				RAISEFIELDERROR_1PARAM( ERR_INVALID_FIELD_DATA, value->GetFieldNumber(), value );
			sval2 = temp->buffer_offset;
			tsize = temp->CalcStringSize();
		} else {
			sval2 = "";
			tsize = 0;
		};

		sval1 = buffer_offset;
		ssize = CalcStringSize();

		comptype = 0;
	} else if (type == LITERAL_VAL && (value->type == STRING || value->type == LITERAL_STRING)) {
		long lit_val = GetLong( true );
		if (lit_val != NOT_IN_LIT_GROUP) {
			XCField* temp = owner->APPDIC->FromLitGroup( GetLong(true), GetDecPlaces() );
			if (!temp)
				RAISEFIELDERROR_1PARAM( ERR_INVALID_FIELD_DATA, GetFieldNumber(), this );

			sval1 = temp->buffer_offset;
			ssize = temp->CalcStringSize();
		} else {
			sval1 = "";
			ssize = 0;
		};

		sval2 = value->buffer_offset;
		tsize = value->CalcStringSize();

		comptype = 0;

//		cout << "Comparing lit to string: " << endl;
//		cout << "\tStr1: '" << sval1 << "'" << endl;
//		cout << "\tStr2: '" << sval2 << "'" << endl;
	} else if
		((data_type_defns[ type ].type_group & TYPE_DATE ||
		data_type_defns[ value->type ].type_group & TYPE_DATE) ||
		(data_type_defns[ type ].type_group & TYPE_TIME ||
		data_type_defns[ value->type ].type_group & TYPE_TIME)) {
			// Dates/times can be handled as julian values
			dval1 = ToJulian();
			dval2 = value->ToJulian();
			comptype = 2;
	} else if (data_type_defns[ type ].type_group & TYPE_STRING &&
		data_type_defns[ value->type ].type_group & TYPE_STRING) {

		// Default to string type comparison
		sval1 = buffer_offset;
		sval2 = value->buffer_offset;

		ssize = CalcStringSize();
		tsize = value->CalcStringSize();

		comptype = 0;
	} else if (data_type_defns[ type ].type_group & TYPE_DUR &&
		data_type_defns[ value->type ].type_group & TYPE_DUR) {

		// Convert to double values
		dval1 = DurationToDouble();
		dval2 = value->DurationToDouble();
		comptype = 2;
	};


	switch( comptype ) {
		case 0: {		// String comparisons
			// See what size compare we should use
			assert( sval1 );
			assert( sval2 );

			owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Compare strings:  (Field:%s,%s) Size = %d,%d:\n\tString1: '%s'\n\tString2: '%s'", GetFieldName(), value->GetFieldName(), ssize, tsize, sval1, sval2 );
			result = strcmp( sval1, sval2 );
			break;
		};

		case 1:		// Long comparisons
			owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Compare long: %d <--> %d (Field:%s,%s)", lval1, lval2, GetFieldName(), value->GetFieldName() );
			if (lval1 > lval2)
				result = 1;
			else if (lval1 == lval2)
				result = 0;
			else 
				result = -1;
			break;

		case 2: {		// Double comparisons
			owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Compare double: %g <--> %g (Field:%s,%s)", dval1, dval2, GetFieldName(), value->GetFieldName() );
			double temp = dval1 - dval2;
			if (fabs( temp ) < EPSILON)
				result = 0;
			else if (temp > 0)
				result = 1;
			else
				result = -1;
			break;
		};

		case 3: {		// Boolean
			owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Compare boolean: %d <--> %d (Field:%s,%s)", lval1, lval2, GetFieldName(), value->GetFieldName() );
			if ((lval1 != 0) == (lval2 != 0))
				result = 0;
			else
				result = 1;
			break;
		};

		case 4: 		// Binary CRC check
			owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Compare binary (CRC): %ld bytes at %x <--> %ld bytes at %x (Field:%s,%s)", ssize, bval1, tsize, bval2, GetFieldName(), value->GetFieldName());
			if( ssize != tsize )
				result = 1;
			else {
				XCcrc32 BlobCRC;
				long crc1, crc2;
				crc1 = BlobCRC.Get_CRC32( (unsigned char *)bval1, ssize );
				crc2 = BlobCRC.Get_CRC32( (unsigned char *)bval2, tsize );
				if( crc1 != crc2 )
					result = 1;
				else
					result = 0;
			}
			break;

		default:	// Unsupported comparisons
			RAISEFIELDERROR_4PARAM( ERR_INVALID_COMPARISON, GetFieldNumber(), type, this, value->type, value );
	};
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "\tResult = %d", result );

	EXT_OUTFUNCRET( result );
};






bool XCField::CheckInRange( const long minval, const long maxval ) const {
	EXT_INFUNC( XCField::CheckInRange, owner->APPDIC );

	long value = GetLong( true );
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "  Check: %d <= %d <= %d", minval, value, maxval );

	EXT_OUTFUNCRET( value >= minval && value <= maxval );
};




int XCField::Compare( const long test_value ) const {
	EXT_INFUNC( XCField::Compare, owner->APPDIC );
	int result;
	long value = GetLong( true );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "  Values: %d <==> %d", value, test_value );
	if (value > test_value)
		result = 1;
	else if (value < test_value)
		result = -1;
	else
		result = 0;

	EXT_OUTFUNCRET( result );
};

int XCField::CompareDecimal( double test_value ) const {
	EXT_INFUNC( XCField::Compare, owner->APPDIC );
	int result;
	double value = GetDouble( true );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "  Values: %f <==> %f", value, test_value );
	double temp = value - test_value;
	if (fabs( temp ) < EPSILON)
		result = 0;
	else if (temp > 0)
		result = 1;
	else
		result = -1;

	EXT_OUTFUNCRET( result );
};

int XCField::CompareZero() const {
	EXT_INFUNC( XCField::CompareZero, owner->APPDIC );
	int result;
	double dvalue = 0;
	long lvalue = 0;

	bool whole_number = true;

	switch( type ) {
		case S_INTEGER:
		case D_INTEGER:
		case LITERAL_VAL:
		case AUTONUMBER:
			lvalue = GetLong( true );
			whole_number = true;
			break;

		case DEC_FLOAT:
		case DEC_DOUBLE:
			dvalue = GetDouble( true );
			owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Values: %g <--> 0.0", dvalue );
			whole_number = false;
			break;

		case DISPLAY_NUM: {
			char* chartemp = buffer_offset;
			chartemp[ size ] = 0;

			if (!GetDecPlaces()) {
				lvalue = GetLong( true );
				whole_number = true;
			} else {
				dvalue = GetDouble( true );
				whole_number = false;
			};
			break;
		};

		case DUR_HH_TIME_DISP:
		case DUR_HM_TIME_DISP:
			dvalue = DurationToDouble();
			whole_number = false;
			break;

		default:
			RAISEFIELDERROR_1PARAM( ERR_UNSUPPORTED_COMPARISON, GetFieldNumber(), this );
	};


	if (whole_number) {
		owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "  Values: %d <==> 0", lvalue );
		if (lvalue > 0)
			result = 1;
		else if (lvalue < 0)
			result = -1;
		else
			result = 0;
	} else {
		if (fabs( dvalue ) < EPSILON)
			result = 0;
		else if (dvalue > 0)
			result = 1;
		else
			result = -1;
	};

	EXT_OUTFUNCRET( result );
};

bool XCField::CompareLiteral( long lit_num ) const {
	EXT_INFUNC( XCField::CompareLiteral, owner->APPDIC );

	XCField* lit = owner->APPDIC->GetLiteral( lit_num );
	EXT_OUTFUNCRET( CompareField( lit )==0 );
};




void XCField::AdjustDate( date_conv_type& date ) {
	INFUNC( XCField::AdjustDate, owner->APPDIC );

	date_conv_type value;

	// First, check that field has a current value.  Doesn't make sense to add values
	// to a blank or NULL date
	if (IsBlank())
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 0 );

	// Convert from the current type to a julian
	double jul_date = ToJulian();

	jul_date += (1 * date.day);
	jul_date += (30 * date.month);
	jul_date += (365 * date.year);
/*
	// First, convert to a gregorian date
	if (!convert_to_gregorian( jul_date, value, &err ))
		RAISETOOLSERROR( err );

	// Now, adjust the date and normalize the structure


	// Next, convert from a gregorian date back to a julian
	if (!convert_to_julian( jul_date, value, &err ))
		RAISETOOLSERROR( err );
*/
	// Now, go back to whatever type we want
	FromJulian( jul_date );

	OUTFUNC();
};



void XCField::AddDays( const XCField* source ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.day = source->GetLong( true );
	AdjustDate( date );
};

void XCField::AddConstDays( long value ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.day = value;
	AdjustDate( date );
};

void XCField::SubDays( const XCField* source ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.day = -source->GetLong( true );
	AdjustDate( date );
};


void XCField::AddMonths( const XCField* source ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.month = source->GetLong( true );
	AdjustDate( date );
};

void XCField::AddConstMonths( long value ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.month = value;
	AdjustDate( date );
};

void XCField::SubMonths( const XCField* source ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.month = -source->GetLong( true );
	AdjustDate( date );
};

void XCField::AddYears( const XCField* source ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.year = source->GetLong( true );
	AdjustDate( date );
};

void XCField::AddConstYears( long value ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.year = value;
	AdjustDate( date );
};

void XCField::SubYears( const XCField* source ) {
	date_conv_type date;
	memset( &date, 0, sizeof( date_conv_type ) );

	// Call the date adjustment routine
	date.year = -source->GetLong( true );
	AdjustDate( date );
};
